package com.zbs.framework.dao.utils.jpa;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @author: Zbs
 * @create: 2020-09-10 14:31
 * @description: Operator 参数
 *      criteria 查询条件构造器
 *      selectors 字段选择器，可以指定字段查询
 *      使用selectors时，必须创建参数构造器
 *
 *
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Operator {

    private String column;
    private Object value;
    private ConditionEnum type;

    private Join join;

    enum Join {
        or,
        and
    }

    private List<Operator> criteria = new ArrayList<>();
    private List<Operator> selectors = new ArrayList<>();
    private List<Operator> orCriteria = new ArrayList<>();
    private Map<Integer,List<Operator>> orOperator = new LinkedHashMap<>();

    public static Operator build(){
        return new Operator();
    }

    private Operator(String column){
        this.column = column;
    }

    public void clearCriteria(){
        this.criteria.clear();
    }

    public void clearOrCriteria(){
        this.orCriteria.clear();
    }


    public List<Operator> getCriteria() {
        return criteria;
    }

    private Operator(Join join, String column, Object value, ConditionEnum type) {
        this.join = join;
        this.column = column;
        this.value = value;
        this.type = type;
    }

    private Operator criteria(Join join, String column, Object value, ConditionEnum type) {
        this.criteria.add(new Operator(join, column, value, type));
        return this;
    }

    public Operator selectors(String... column) {
        for (String s : column) {
            this.selectors.add(new Operator(s));
        }
        return this;
    }

    public Operator or(List<Operator> orCriteria){
        Integer maxKey = ColumnUtil.getMaxKey(orOperator);
        this.getOrOperator().put(maxKey == null ? 1 : maxKey +1,orCriteria);
        return this;
    }

    public Operator orEqual(String column, Object value){
        return this.orEqual(column, value,true);
    }

    public Operator orEqual(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.equal));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.equal));
        }
        return this;
    }

    public Operator equal(String column, Object value){
        return this.equal(column, value,true);
    }

    public Operator equal(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.equal));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.equal));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.equal));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.equal));
        }
        return this;
    }

    public Operator orNotEqual(String column, Object value){
        return this.orNotEqual(column, value, true);
    }

    public Operator orNotEqual(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.notEqual));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.notEqual));
        }
        return this;
    }

    public Operator notEqual(String column, Object value){
        return this.notEqual(column, value,true);
    }

    public Operator notEqual(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.notEqual));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.notEqual));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.notEqual));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.notEqual));
        }

        return this;
    }

    public Operator orGt(String column, Object value){
        return this.orGt(column, value,true);
    }

    public Operator orGt(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.gt));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.gt));
        }

        return this;
    }

    public Operator gt(String column, Object value){
        return this.gt(column, value,true);
    }
    public Operator gt(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.gt));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.gt));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.gt));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.gt));
        }
        return this;
    }

    public Operator orGe(String column, Object value){
        return this.orGe(column, value, true);
    }

    public Operator orGe(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.ge));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.ge));
        }
        return this;
    }

    public Operator ge(String column, Object value){
        return this.ge(column, value, true);
    }

    public Operator ge(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.ge));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.ge));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.ge));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.ge));
        }
        return this;
    }

    public Operator orLt(String column, Object value){
        return this.orLt(column, value, true);
    }

    public Operator orLt(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lt));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lt));
        }

        return this;
    }

    public Operator lt(String column, Object value){
        return this.lt(column, value, true);
    }

    public Operator lt(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lt));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lt));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lt));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lt));
        }
        return this;
    }

    public Operator orLe(String column, Object value){
        return this.orLe(column, value, true);
    }

    public Operator orLe(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.le));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.le));
        }
        return this;
    }

    public Operator le(String column, Object value){
        return this.le(column, value, true);
    }

    public Operator le(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.le));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.le));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.le));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.le));
        }
        return this;
    }

    public Operator orLike(String column, Object value){
        return this.orLike(column, value, true);
    }

    public Operator orLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.like));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.like));
        }
        return this;
    }

    public Operator like(String column, Object value){
        return this.like(column, value, true);
    }

    public Operator like(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.like));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.like));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.like));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.like));
        }
        return this;
    }

    public Operator orLeLike(String column, Object value){
        return this.orLeLike(column, value, true);
    }

    public Operator orLeLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.leLike));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.leLike));
        }
        return this;
    }

    public Operator leLike(String column, Object value){
        return this.leLike(column, value, true);
    }

    public Operator leLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.leLike));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.leLike));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.leLike));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.leLike));
        }
        return this;
    }

    public Operator orGeLike(String column, Object value){
        return this.orGeLike(column, value, true);
    }


    public Operator orGeLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.geLike));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.geLike));
        }
        return this;
    }

    public Operator geLike(String column, Object value){
        return this.geLike(column, value, true);
    }

    public Operator geLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.geLike));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.geLike));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.geLike));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.geLike));
        }
        return this;
    }

    public Operator orNotLike(String column, Object value){
        return this.orNotLike(column, value, true);
    }

    public Operator orNotLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.notLike));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.notLike));
        }
        return this;
    }

    public Operator notLike(String column, Object value){
        return this.notLike(column, value, true);
    }

    public Operator notLike(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.notLike));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.notLike));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.notLike));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.notLike));
        }
        return this;
    }



    public Operator orIsNull(String column) {
        this.criteria.add(new Operator(Join.or, column, null, ConditionEnum.isNull));
        return this;
    }


    public Operator isNull(String column) {
        this.criteria.add(new Operator(Join.and, column, null, ConditionEnum.isNull));
        this.orCriteria.add(new Operator(Join.and, column, null, ConditionEnum.isNull));
        return this;
    }


    public Operator orIsNotNull(String column) {
        this.criteria.add(new Operator(Join.or, column, null, ConditionEnum.isNotNull));
        return this;
    }

    public Operator isNotNull(String column) {
        this.criteria.add(new Operator(Join.and, column, null, ConditionEnum.isNotNull));
        this.orCriteria.add(new Operator(Join.and, column, null, ConditionEnum.isNotNull));
        return this;
    }

    public Operator orGreaterThan(String column, Object value){
        return this.orGreaterThan(column, value, true);
    }

    public Operator orGreaterThan(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.greaterThan));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.greaterThan));
        }
        return this;
    }

    public Operator greaterThan(String column, Object value){
        return this.greaterThan(column, value, true);
    }

    public Operator greaterThan(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThan));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThan));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThan));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThan));
        }
        return this;
    }

    public Operator orGreaterThanOrEqualTo(String column, Object value){
        return this.orGreaterThanOrEqualTo(column, value, true);
    }

    public Operator orGreaterThanOrEqualTo(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.greaterThanOrEqualTo));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.greaterThanOrEqualTo));
        }
        return this;
    }

    public Operator greaterThanOrEqualTo(String column, Object value){
        return this.greaterThanOrEqualTo(column, value, true);
    }

    public Operator greaterThanOrEqualTo(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThanOrEqualTo));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThanOrEqualTo));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThanOrEqualTo));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.greaterThanOrEqualTo));
        }
        return this;
    }

    public Operator orLessThan(String column, Object value){
        return this.orLessThan(column, value, true);
    }

    public Operator orLessThan(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lessThan));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lessThan));
        }
        return this;
    }

    public Operator lessThan(String column, Object value){
        return this.lessThan(column, value, true);
    }

    public Operator lessThan(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThan));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThan));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThan));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThan));
        }
        return this;
    }

    public Operator orLessThanOrEqualTo(String column, Object value){
        return this.orLessThanOrEqualTo(column, value, true);
    }

    public Operator orLessThanOrEqualTo(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lessThanOrEqualTo));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.lessThanOrEqualTo));
        }
        return this;
    }

    public Operator lessThanOrEqualTo(String column, Object value){
        return this.lessThanOrEqualTo(column, value, true);
    }

    public Operator lessThanOrEqualTo(String column, Object value, Boolean flag) {
        if (flag) {
            if (!ObjectUtils.isEmpty(value)) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThanOrEqualTo));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThanOrEqualTo));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThanOrEqualTo));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.lessThanOrEqualTo));
        }
        return this;
    }



    public Operator orNotIn(String column, Collection<?> value) {
        if (!CollectionUtils.isEmpty(value)) {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.notIn));
        }
        return this;
    }


    public Operator notIn(String column, Collection<?> value) {
        if (!CollectionUtils.isEmpty(value)) {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.notIn));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.notIn));
        }
        return this;
    }

    public Operator orIn(String column, Collection<?> value, Boolean flag) {
        if (!CollectionUtils.isEmpty(value)) {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.in));
        }
        return this;
    }

    public Operator in(String column, Collection<?> value) {
        if (!CollectionUtils.isEmpty(value)) {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.in));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.in));
        }
        return this;
    }

    public Operator orDateBetween(String column, Date[] value){
        return this.orDateBetween(column, value, true);
    }

    public Operator orDateBetween(String column, Date[] value, Boolean flag) {
        if (flag) {
            if (value != null) {
                this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.dateBetween));
            }
        } else {
            this.criteria.add(new Operator(Join.or, column, value, ConditionEnum.dateBetween));
        }
        return this;
    }

    public Operator dateBetween(String column, Date[] value) {
        this.dateBetween(column, value,true);
        return this;
    }

    public Operator dateBetween(String column, Date[] value, Boolean flag) {
        if (flag) {
            if (value[0] != null && value[1] != null) {
                this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.dateBetween));
                this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.dateBetween));
            }
        } else {
            this.criteria.add(new Operator(Join.and, column, value, ConditionEnum.dateBetween));
            this.orCriteria.add(new Operator(Join.and, column, value, ConditionEnum.dateBetween));
        }
        return this;
    }

}
